package com.gitee.melin.bee.core.service;

import com.gitee.melin.bee.core.hibernate5.HibernateBaseDao;
import com.gitee.melin.bee.core.model.BaseEntity;
import com.gitee.melin.bee.core.model.IEntity;
import com.gitee.melin.bee.core.spring.security.LoginUserDetails;
import com.gitee.melin.bee.core.support.Pagination;
import com.gitee.melin.bee.core.support.PaginationRequest;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.Instant;
import java.util.List;
import org.hibernate.criterion.*;
import org.hibernate.engine.jdbc.LobCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;

/**
 * 服务层，公共类
 *
 * @author admin@gmail.com
 */
public abstract class BaseServiceImpl<T extends IEntity, ID extends Serializable>
        implements BaseService<T, ID>, InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(BaseServiceImpl.class);

    protected Class<T> entityClass;

    protected String entityName;

    protected boolean isAssignableBaseEntity = true;

    public abstract HibernateBaseDao<T, ID> getHibernateBaseDao();

    private static boolean hasSecurityJar = true;

    static {
        try {
            Class.forName("org.springframework.security.core.Authentication");
        } catch (Throwable e) {
            hasSecurityJar = false;
            logger.warn("spring-security-core.jar not exist");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            entityClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
            entityName = entityClass.getSimpleName();
            isAssignableBaseEntity = ClassUtils.isAssignable(BaseEntity.class, entityClass);
        }
    }

    @Transactional(rollbackFor = Exception.class, readOnly = true)
    @Override
    public T getEntity(ID id) {
        return this.getHibernateBaseDao().get(id);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findAllEntity() {
        return this.getHibernateBaseDao().loadAll();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findAllEntityByOrder(Order... orders) {
        return this.getHibernateBaseDao().loadAll(orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findAllEntity(Projection projection) {
        return this.getHibernateBaseDao().findByNamedParam(projection, new String[] {}, new Object[] {});
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ID insertEntity(T entity) {
        if (hasSecurityJar) {
            setCreaterAndTime(entity);
        } else {
            setCreateTime(entity);
        }
        return this.getHibernateBaseDao().save(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateEntity(T entity) {
        if (hasSecurityJar) {
            setModifierAndTime(entity);
        } else {
            setUpdateTime(entity);
        }
        this.getHibernateBaseDao().update(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public T mergeEntity(T entity) {
        return this.getHibernateBaseDao().merge(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public T deleteEntity(ID id) {
        return this.getHibernateBaseDao().delete(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteEntity(T entity) {
        this.getHibernateBaseDao().delete(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteEntity(String key, Object value) {
        String hql = "delete " + entityName + " where " + key + " = :" + key;
        this.deleteOrUpdateByHQL(hql, key, value);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteEntity(String key1, Object value1, String key2, Object value2) {
        String hql = "delete " + entityName + " where " + key1 + " = :" + key1 + " and " + key2 + " = :" + key2;
        this.deleteOrUpdateByHQL(hql, key1, value1, key2, value2);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteEntity(String key1, Object value1, String key2, Object value2, String key3, Object value3) {
        String hql = "delete "
                + entityName
                + " where "
                + key1
                + " = :"
                + key1
                + " and "
                + key2
                + " = :"
                + key2
                + " and "
                + key3
                + " = :"
                + key3;
        this.deleteOrUpdateByHQL(hql, new String[] {key1, key2, key3}, new Object[] {value1, value2, value3});
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public T logicDeleteEntity(ID id) {
        T entity = this.getEntity(id);
        if (entity instanceof BaseEntity) {
            BaseEntity be = (BaseEntity) entity;
            // be.setDelFlag(1);
            this.updateEntity((T) be);
        }

        return entity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void bulkDeleteEntity(ID[] ids) {
        for (ID id : ids) {
            this.deleteEntity(id);
        }
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> loadEntities() {
        return this.getHibernateBaseDao().loadAll();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String propertyName, Object value) {
        return this.getHibernateBaseDao().findByNamedParam(propertyName, value);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(Projection projection) {
        return this.getHibernateBaseDao().findByNamedParam(projection, new String[] {}, new Object[] {});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(Projection projection, String propertyName, Object value) {
        return this.getHibernateBaseDao().findByNamedParam(projection, propertyName, value);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String[] columns, String propertyName, Object value) {
        Projection projection = buildProjectionList(columns);
        return this.getHibernateBaseDao().findByNamedParam(projection, propertyName, value);
    }

    private static ProjectionList buildProjectionList(String[] columns) {
        ProjectionList projectionList = null;
        if (columns != null && columns.length > 0) {
            projectionList = Projections.projectionList();
            for (String column : columns) {
                projectionList.add(Projections.property(column).as(column));
            }
        }
        return projectionList;
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String propertyName1, Object value1, String propertyName2, Object value2) {
        return this.getHibernateBaseDao()
                .findByNamedParam(new String[] {propertyName1, propertyName2}, new Object[] {value1, value2});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(
            String propertyName1,
            Object value1,
            String propertyName2,
            Object value2,
            String propertyName3,
            Object value3) {
        return this.getHibernateBaseDao()
                .findByNamedParam(
                        new String[] {propertyName1, propertyName2, propertyName3},
                        new Object[] {value1, value2, value3});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public T queryByNamedParam(String propertyName, Object value) {
        return this.getHibernateBaseDao().queryByNamedParam(propertyName, value);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public T queryByNamedParam(String propertyName1, Object value1, String propertyName2, Object value2) {
        return this.getHibernateBaseDao()
                .queryByNamedParam(new String[] {propertyName1, propertyName2}, new Object[] {value1, value2});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public T queryByNamedParam(
            String propertyName1,
            Object value1,
            String propertyName2,
            Object value2,
            String propertyName3,
            Object value3) {
        return this.getHibernateBaseDao()
                .queryByNamedParam(
                        new String[] {propertyName1, propertyName2, propertyName3},
                        new Object[] {value1, value2, value3});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String[] propertyNames, Object[] values) {
        return this.getHibernateBaseDao().findByNamedParam(propertyNames, values);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(Projection projection, String[] propertyNames, Object[] values) {
        return this.getHibernateBaseDao().findByNamedParam(projection, propertyNames, values);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String[] columns, String[] propertyNames, Object[] values) {
        Projection projection = buildProjectionList(columns);
        return this.getHibernateBaseDao().findByNamedParam(projection, propertyNames, values);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public T queryByNamedParam(String[] propertyNames, Object[] values) {
        return this.getHibernateBaseDao().queryByNamedParam(propertyNames, values);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(List<String> propertyNames, List<Object> values) {
        return this.getHibernateBaseDao().findByNamedParam(propertyNames.toArray(new String[] {}), values.toArray());
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(String propertyName, Object value, Order... orders) {
        return this.getHibernateBaseDao().findByNamedParamAndOrder(propertyName, value, orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(String[] propertyNames, Object[] values, Order... orders) {
        return this.getHibernateBaseDao().findByNamedParamAndOrder(propertyNames, values, orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(
            Projection projection, String[] propertyNames, Object[] values, Order... orders) {
        return this.getHibernateBaseDao().findByNamedParamAndOrder(projection, propertyNames, values, orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(
            String[] columns, String[] propertyNames, Object[] values, Order... orders) {
        Projection projection = buildProjectionList(columns);
        return this.getHibernateBaseDao().findByNamedParamAndOrder(projection, propertyNames, values, orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(List<String> propertyNames, List<Object> values, Order... orders) {
        return this.getHibernateBaseDao()
                .findByNamedParamAndOrder(propertyNames.toArray(new String[] {}), values.toArray(), orders);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(String joinEntity, String propertyName, Object value, Order order) {
        return this.findByNamedParamAndOrder(new String[] {joinEntity}, propertyName, value, order);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(String[] joinEntitys, String propertyName, Object value, Order order) {
        return this.getHibernateBaseDao()
                .findByNamedParamAndOrder(
                        joinEntitys, new String[] {propertyName}, new Object[] {value}, new Order[] {order});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(
            String[] joinEntitys, String[] propertyNames, Object[] values, Order order) {
        return this.getHibernateBaseDao()
                .findByNamedParamAndOrder(joinEntitys, propertyNames, values, new Order[] {order});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findByNamedParamAndOrder(String propertyName, Object value, Order order, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParamAndOrder(propertyName, value, order, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParam(String[] propertyNames, Object[] values, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findByNamedParam(propertyNames, values, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByNamedParamAndOrder(
            String[] propertyNames, Object[] values, Order[] orders, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findByNamedParamAndOrder(propertyNames, values, orders, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPage(int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByExample(offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPage(PaginationRequest<T> paginationRequest) {
        return this.getHibernateBaseDao().findPage(paginationRequest);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageAndOrder(Order[] orders, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageAndOrderByExample(orders, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(
            String joinEntity, String propertyName, Object value, final int page, final int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParam(joinEntity, propertyName, value, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(String propertyName, Object value, final int page, final int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParam(propertyName, value, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(
            String propertyName1, Object value1, String propertyName2, Object value2, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao()
                .findPageByNamedParam(
                        new String[] {propertyName1, propertyName2}, new Object[] {value1, value2}, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            String propertyName1,
            Object value1,
            String propertyName2,
            Object value2,
            Order order,
            int page,
            int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao()
                .findPageByNamedParamAndOrder(
                        new String[] {propertyName1, propertyName2},
                        new Object[] {value1, value2},
                        new Order[] {order},
                        offset,
                        limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            String propertyName, Object value, Order order, final int page, final int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParamAndOrder(propertyName, value, order, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(
            String[] propertyNames, Object[] values, final int page, final int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParam(propertyNames, values, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(
            Projection projection, String[] propertyNames, Object[] values, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParam(projection, propertyNames, values, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(
            String[] columns, String[] propertyNames, Object[] values, int page, int limit) {
        int offset = (page - 1) * limit;
        Projection projection = buildProjectionList(columns);
        return this.getHibernateBaseDao().findPageByNamedParam(projection, propertyNames, values, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParam(List<String> propertyNames, List<Object> values, int page, int limit) {
        return this.findPageByNamedParam(propertyNames.toArray(new String[] {}), values.toArray(), page, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            String[] propertyNames, Object[] values, Order[] orders, final int page, final int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao().findPageByNamedParamAndOrder(propertyNames, values, orders, offset, limit);
    }

    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            Projection projection, String[] propertyNames, Object[] values, Order[] orders, int page, int limit) {
        int offset = (page - 1) * limit;
        return this.getHibernateBaseDao()
                .findPageByNamedParamAndOrder(projection, propertyNames, values, orders, offset, limit);
    }

    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            String[] columns, String[] propertyNames, Object[] values, Order[] orders, int page, int limit) {
        int offset = (page - 1) * limit;
        Projection projection = buildProjectionList(columns);
        return this.getHibernateBaseDao()
                .findPageByNamedParamAndOrder(projection, propertyNames, values, orders, offset, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Pagination<T> findPageByNamedParamAndOrder(
            List<String> propertyNames, List<Object> values, List<Order> orders, final int page, final int limit) {
        return this.findPageByNamedParamAndOrder(
                propertyNames.toArray(new String[] {}), values.toArray(), orders.toArray(new Order[] {}), page, limit);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount() {
        return this.getHibernateBaseDao().findCount();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount(Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        return this.getHibernateBaseDao().findCountByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount(String propertyName, Object value) {
        return this.getHibernateBaseDao().findCountByNamedParam(propertyName, value);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount(String propertyName1, Object value1, String propertyName2, Object value2) {
        return this.getHibernateBaseDao()
                .findCountByNamedParam(new String[] {propertyName1, propertyName2}, new Object[] {value1, value2});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount(
            String propertyName1,
            Object value1,
            String propertyName2,
            Object value2,
            String propertyName3,
            Object value3) {
        return this.getHibernateBaseDao()
                .findCountByNamedParam(
                        new String[] {propertyName1, propertyName2, propertyName3},
                        new Object[] {value1, value2, value3});
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public Long queryCount(String[] propertyNames, Object[] values) {
        return this.getHibernateBaseDao().findCountByNamedParam(propertyNames, values);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer deleteOrUpdateByHQL(final String hql, final String paramName, final Object value) {
        return this.getHibernateBaseDao().deleteOrUpdateByHQL(hql, paramName, value);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer deleteOrUpdateByHQL(
            String hql, String propertyName1, Object value1, String propertyName2, Object value2) {
        return this.getHibernateBaseDao()
                .deleteOrUpdateByHQL(hql, new String[] {propertyName1, propertyName2}, value1, value2);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Integer deleteOrUpdateByHQL(final String hql, final String[] paramNames, final Object[] values) {
        return this.getHibernateBaseDao().deleteOrUpdateByHQL(hql, paramNames, values);
    }

    @Override
    public LobCreator getLobCreator() {
        return this.getHibernateBaseDao().getLobCreator();
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public T queryByCriterions(Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        return this.getHibernateBaseDao().findByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public <R> R queryByCriterions(Projection projection, Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        if (projection != null) {
            detachedCriteria.setProjection(projection);
        }
        return this.getHibernateBaseDao().findByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByCriterions(Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        return this.getHibernateBaseDao().findListByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public <R> List<R> findByCriterion(Projection projection, Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        if (projection != null) {
            detachedCriteria.setProjection(projection);
        }
        return this.getHibernateBaseDao().findListByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<T> findByCriterions(Order order, Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        if (order != null) {
            detachedCriteria.addOrder(order);
        }
        return this.getHibernateBaseDao().findListByCriteria(detachedCriteria);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public <R> List<R> findByCriterion(Projection projection, Order order, Criterion... criterions) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
        for (Criterion criterion : criterions) {
            detachedCriteria.add(criterion);
        }
        if (order != null) {
            detachedCriteria.addOrder(order);
        }
        if (projection != null) {
            detachedCriteria.setProjection(projection);
        }
        return this.getHibernateBaseDao().findListByCriteria(detachedCriteria);
    }

    private void setCreaterAndTime(T entity) {
        if (entity instanceof BaseEntity && SecurityContextHolder.getContext().getAuthentication() != null) {

            BaseEntity baseEntity = (BaseEntity) entity;
            baseEntity.setGmtCreated(Instant.now());
            baseEntity.setGmtModified(Instant.now());

            Object principal =
                    SecurityContextHolder.getContext().getAuthentication().getPrincipal();

            if (principal instanceof LoginUserDetails) {
                LoginUserDetails userDetails = (LoginUserDetails) principal;
                String name = userDetails.getUsername();
                baseEntity.setCreater(name);
                baseEntity.setModifier(name);
            }
        }
    }

    private void setCreateTime(T entity) {
        if (entity instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) entity;
            baseEntity.setGmtCreated(Instant.now());
            baseEntity.setGmtModified(Instant.now());
        }
    }

    private void setModifierAndTime(T entity) {
        if (entity instanceof BaseEntity && SecurityContextHolder.getContext().getAuthentication() != null) {

            BaseEntity baseEntity = (BaseEntity) entity;
            baseEntity.setGmtModified(Instant.now());

            Object principal =
                    SecurityContextHolder.getContext().getAuthentication().getPrincipal();

            if (principal instanceof LoginUserDetails) {
                LoginUserDetails userDetails = (LoginUserDetails) principal;
                String name = userDetails.getUsername();
                baseEntity.setModifier(name);
            }
        }
    }

    private void setUpdateTime(T entity) {
        if (entity instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) entity;
            baseEntity.setGmtModified(Instant.now());
        }
    }
}
